package span

import (
	"fmt"
)

// Span represents a session code range in standardized form.
type Span struct {
	Start Position
	End   Position
}

func (s Span) IsValid() bool {
	return s.Start.IsValid() && s.End.IsValid()
}

func (s Span) ToUTF8Span(content []byte) (UTF8Span, error) {
	return s.ToUTF8SpanWithCache(content, GetLineStarts(content))
}

func (s Span) ToUTF8SpanWithCache(content []byte, lineStarts []int) (UTF8Span, error) {
	start, err := s.Start.ToUTF8PositionWithCache(content, lineStarts)
	if err != nil {
		return UTF8Span{}, err
	}
	end, err := s.End.ToUTF8PositionWithCache(content, lineStarts)
	if err != nil {
		return UTF8Span{}, err
	}
	return UTF8Span{Start: start, End: end}, nil
}

func (s Span) ToUTF16Span(content []byte) (UTF16Span, error) {
	return s.ToUTF16SpanWithCache(content, GetLineStarts(content))
}

func (s Span) ToUTF16SpanWithCache(content []byte, lineStarts []int) (UTF16Span, error) {
	start, err := s.Start.ToUTF16PositionWithCache(content, lineStarts)
	if err != nil {
		return UTF16Span{}, err
	}
	end, err := s.End.ToUTF16PositionWithCache(content, lineStarts)
	if err != nil {
		return UTF16Span{}, err
	}
	return UTF16Span{Start: start, End: end}, nil
}

// Position represents a single point within a file.
// In general this should only be used as part of a Span, as on its own it
// does not carry enough information.
type Position struct {
	Line   int
	Column int
}

func (p Position) IsValid() bool {
	return p.Line >= 0 && p.Column >= 0
}

func (p Position) ToOffset(content []byte) (int, error) {
	return p.ToOffsetWithCache(GetLineStarts(content))
}

func (p Position) ToOffsetWithCache(lineStarts []int) (int, error) {
	if p.Line >= len(lineStarts) {
		return -1, fmt.Errorf("ToOffsetWithCache: line out of range")
	}
	return lineStarts[p.Line] + p.Column, nil
}

func (p Position) ToUTF8Position(content []byte) (UTF8Position, error) {
	return ToUTF8Position(p, content, GetLineStarts(content))
}

func (p Position) ToUTF8PositionWithCache(content []byte, lineStarts []int) (UTF8Position, error) {
	return ToUTF8Position(p, content, lineStarts)
}

func (p Position) ToUTF16Position(content []byte) (UTF16Position, error) {
	return ToUTF16Position(p, content, GetLineStarts(content))
}

func (p Position) ToUTF16PositionWithCache(content []byte, lineStarts []int) (UTF16Position, error) {
	return ToUTF16Position(p, content, lineStarts)
}